/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author berthise
 */
package chat.client;

import java.net.*;
import java.io.*;
import chat.client.resources.*;
import chat.common.*;

/**
 * Connection to send a file.
 * @author seb
 */
public class SendFile extends Thread {

    private Socket socket;
    private String nomFichier;
    private BufferedOutputStream sortie;
    private BufferedInputStream entree;
    private ChatView client = null;
    private ServerSocket socketEcoute = null;
    private boolean server;
    private String login;
    private File file = null;
    private ByteArrayOutputStream tableaubytes = null;
    private OutputStream out;
    private InputStream in;
    private boolean ready;
    private long sizeFile;

    /**
     * Le thread, different si c'est un server ou un client.
     */
    @Override
    public void run() {
        ready = false;
        if (server) {
            sendTheFile();
        } else {
            receiveFile();
        }
    }

    /**
     * Constructeur de SendFile si c'est un client
     * @param client le parent
     * @param id le login du binome
     * @param host l'adresse du binome (IP ou dns)
     * @param port numero de port
     * @param nomFichier nom de fichier en chemin absolu
     * @param sizeFile la taille du fichier a transmettre
     */
    public SendFile(ChatView client, String id, String host, int port, String nomFichier, long sizeFile) {
        this.client = client;
        this.nomFichier = nomFichier;
        login = id;
        server = false;
        this.sizeFile = sizeFile;
        client.setNumeroPort(client.getNumeroPort()+7); // pour autre connexion
        try {
            this.socket = new Socket(host, port);
        } catch (Exception e) {
            System.out.println("error le cleint" + e.toString());
        }
    }

    /**
     * Connection des flux (buffer) avec le server
     * On recupere chaque vague de byte dans le flux que l'on transmet directement
     * dans le fichier nomFichier.
     * Avec un artifice, on actualise la barre de progression
     * On ferme tout les sockets
     */
    private void receiveFile() {
        try {
            // on active la connection
            in = socket.getInputStream();
            entree = new BufferedInputStream(in);
            // la sortie se fait dans le fichier a monFichier
            out = new FileOutputStream(nomFichier);
            sortie = new BufferedOutputStream(out);
        } catch (Exception e) {
            System.out.println("error le cleint" + e.toString());
        }
        try {
            ProgressBar progressBar = client.addDownloadProgressBar(nomFichier);
            int alreadyRead;
            alreadyRead = entree.read();
            int compteur = 0;
            long progress = 0;

            long nbpassagesuposé = sizeFile / 4096;

            // on s'arrete quand on nous previens de la fin
            while (alreadyRead > -1) {
                sortie.write(alreadyRead);
                alreadyRead = entree.read();
                compteur++;

                if (compteur == 4096) {
                    compteur = 0;
                    progress++;
                    progressBar.refrech((int)((float) progress / nbpassagesuposé * 100));
                    client.getDownloadPanel().revalidate();
                    client.getDownloadPanel().repaint();
                }
            }
            sortie.write(alreadyRead);
            sortie.flush();
            sortie.close();
            entree.close();
            out.flush();
            out.close();
            in.close();
            socket.close();
            client.removeSendFile(this);
            progressBar.getjLabel1().setText(progressBar.getjLabel1().getText() + " (FINI) ");
            client.writeInfo("Download " + nomFichier + " is finish.");
        } catch (Exception e) {
            System.out.println("error send file " + e.toString());

        }
    }

    /**
     * Construteur de SendFile pour le server
     * @param client le parent
     * @param nomFichier le fichier a transmetre
     * @param login le login du binome pour l'identifier
     */
    public SendFile(ChatView client, String nomFichier, String login) {
        this.client = client;
        this.nomFichier = nomFichier;
        this.login = login;
        server = true;
    }

    /**
     * Envoie au binome les information necessaire a la creation du flux
     * Creation des socket, il attend une connection sur le socket d'ecoute.
     * On envoie le fichier par bloque de 4096 byte en le lisant dirrectment
     * dans le fichier nomFichier.
     * Gestion de la bar de progression
     * Fermeture des sockets..
     */
    public void sendTheFile() {
        try {
            this.socketEcoute = new ServerSocket(client.getNumeroPort()); // creation du socket d'ecoute
            if (socketEcoute != null) {

                InetAddress adrIP = this.client.getCommunication().getSocket().getInetAddress();
                this.file = new File(this.nomFichier);
                //on récupère les informations de notre connection
                String informations = client.getIdChat() + ":" + adrIP.getHostAddress() + ":" 
                        + client.getNumeroPort() + ":" + nomFichier + ":" + file.length();

                client.setNumeroPort(client.getNumeroPort()+3); // pour autre connexion
                //on crée un message file on envoie les info de notre connexion
                Message mess = new Message(Message.FILE, client.getIdChat(), login, informations);
                mess.sendMessage(client.getCommunication().getSortie());


                // on accepte la connection de notre receveur
                socket = socketEcoute.accept();


                // on cree le flux d'entre sur le fichier nomFichier
                this.in = new BufferedInputStream(new FileInputStream(file));
                this.entree = new BufferedInputStream(in);
                this.out = socket.getOutputStream();
                // pour stoker les bites lu, ce sera la direction du flux de sortie
                this.tableaubytes = new ByteArrayOutputStream();
                this.sortie = new BufferedOutputStream(tableaubytes);

            }
        } catch (Exception e) {
            System.out.println("Problème lors de la lecture du fichier");
            System.out.println(e.toString());
        }
        while (!ready) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                System.out.println("error sleep : " + e.toString());
            }
        }
        try {
            ProgressBar progressBar = client.addDownloadProgressBar(nomFichier);

            long sizefile = file.length();

            System.out.println("Taille : " + sizefile);
            long nbpassagesuposé = sizefile / 4096;

            int alreadyRead = in.read(); 
            int[] aecrire = new int[4096];
            int compteur = 0;
            long progress = 0;

            //Tant qu'on est pas à la fin fichier
            while (alreadyRead > -1) {
                //On lit les données du fichier
                aecrire[compteur] = alreadyRead;
                alreadyRead = in.read();
                compteur++;
                //Quand on a rempli le tableau, on envoie un paquet de 4096 octets
                if (compteur == 4096) {
                    compteur = 0;
                    progress++;
                    //On remplit le tampon
                    for (int x = 0; x < 4096; x++) {
                        sortie.write(aecrire[x]);
                    }
                    //Et on l'envoie
                    out.write(tableaubytes.toByteArray());

                    tableaubytes.reset();
                    progressBar.refrech((int)((float) progress / nbpassagesuposé * 100));
                    client.getDownloadPanel().revalidate();
                    client.getDownloadPanel().repaint();
                }
            }
            //On envoie le dernier paquet, qui ne fait pas forcément 4096 octets
            //On remplit le tampon
            for (int x = 0; x < 4096; x++) {
                //System.out.println("hello bit : " + x);
                sortie.write(aecrire[x]);
            }

            //Et on l'envoie
            sortie.flush();
            out.write(tableaubytes.toByteArray());
            out.flush();

//            System.out.println("Avancement: " + (float) progress / nbpassagesuposé * 100 + "%");
            progressBar.refrech((int)((float) progress / nbpassagesuposé * 100));

            
            in.close();
            sortie.close();
            socket.close();
            this.socketEcoute.close();
            client.removeSendFile(this);
            progressBar.getjLabel1().setText(progressBar.getjLabel1().getText() + " (FINI) ");
            client.writeInfo("Download " + nomFichier + " is finish.");
        } catch (Exception e) {
            System.out.println("Problème lors de la lecture du fichier");
            System.out.println(e.toString());
        }
    }


    /**
     * Fermeture de sockets si on a besoin de fermer la connexion avant la fin du transfert.
     * @return
     */
    public boolean closeSendFile() {
        try {
            System.out.println("close file");
            in.close();
            entree.close();
            out.close();
            sortie.close();
            socket.close();
            if (socketEcoute != null) {
                socketEcoute.close();
            }
            client.removeSendFile(this);
        } catch (Exception ex) {
            System.out.println("error  : " + ex.toString());
            return false;
        }
        return true;
    }

    /**
     *
     * @return
     */
    public String getLogin() {
        return login;
    }

    /**
     *
     * @param login
     */
    public void setLogin(String login) {
        this.login = login;
    }

    /**
     *
     * @return
     */
    public ChatView getClient() {
        return client;
    }

    /**
     *
     * @param client
     */
    public void setClient(ChatView client) {
        this.client = client;
    }

    /**
     *
     * @return
     */
    public BufferedInputStream getEntree() {
        return entree;
    }

    /**
     *
     * @param entree
     */
    public void setEntree(BufferedInputStream entree) {
        this.entree = entree;
    }

    /**
     *
     * @return
     */
    public Socket getSocket() {
        return socket;
    }

    /**
     *
     * @param socket
     */
    public void setSocket(Socket socket) {
        this.socket = socket;
    }

    /**
     *
     * @return
     */
    public BufferedOutputStream getSortie() {
        return sortie;
    }

    /**
     *
     * @param sortie
     */
    public void setSortie(BufferedOutputStream sortie) {
        this.sortie = sortie;
    }

    /**
     *
     * @return
     */
    public ServerSocket getSocketEcoute() {
        return socketEcoute;
    }

    /**
     *
     * @param socketEcoute
     */
    public void setSocketEcoute(ServerSocket socketEcoute) {
        this.socketEcoute = socketEcoute;
    }

    /**
     * 
     * @return
     */
    public boolean isReady() {
        return ready;
    }

    /**
     *
     * @param ready
     */
    public void setReady(boolean ready) {
        this.ready = ready;
    }
}
